/*******************************************************************************************
*
*   raylib [core] example - 2d camera mouse zoom
*
*   Example complexity rating: [★★☆☆] 2/4
*
*   Example originally created with raylib 4.2, last time updated with raylib 4.2
*
*   Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
*
*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
*   BSD-like license that allows static linking with closed source software
*
*   Copyright (c) 2022-2025 Jeffery Myers (@JeffM2501)
*
********************************************************************************************/

#include "raylib.h"   // 引入 Raylib 核心库
#include "rlgl.h"     // 引入 Raylib 的底层图形接口 (OpenGL 抽象层)，用于直接的OpenGL风格绘图
#include "raymath.h"  // 引入 Raylib 的数学函数库，包含向量、矩阵运算等

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main ()
{
    // Initialization
    //--------------------------------------------------------------------------------------
    const int screenWidth = 800;    // 定义屏幕宽度
    const int screenHeight = 450;   // 定义屏幕高度

    InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera mouse zoom"); // 初始化窗口

    Camera2D camera = { 0 }; // 初始化2D相机对象
    camera.zoom = 1.0f;     // 设置相机初始缩放级别为1.0 (不缩放)
                            // camera.target 和 camera.offset 默认为 (0,0)

    int zoomMode = 0;   // 定义缩放模式变量: 0 表示鼠标滚轮缩放, 1 表示鼠标移动缩放

    SetTargetFPS(60);                   // 设置游戏的目标帧率为每秒60帧
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())        // 主游戏循环
    {
        // Update
        //----------------------------------------------------------------------------------
        // 切换缩放模式
        if (IsKeyPressed(KEY_ONE)) zoomMode = 0;    // 按下数字键 '1' 切换到鼠标滚轮缩放模式
        else if (IsKeyPressed(KEY_TWO)) zoomMode = 1; // 按下数字键 '2' 切换到鼠标移动缩放模式
        
        // 相机平移 (拖动) 控制：基于鼠标左键按下并拖动
        if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
        {
            Vector2 delta = GetMouseDelta(); // 获取鼠标自上一帧以来的移动向量 (屏幕坐标)
            // 将屏幕坐标的delta转换为世界坐标的delta。
            // 需要除以当前的缩放级别 camera.zoom，因为屏幕上的1个像素可能对应世界中的多个或部分像素。
            // 乘以 -1.0f 是因为我们希望拖动方向与相机移动方向一致 (即拖动鼠标向右，世界内容向左移动，相机目标点向右移动)
            delta = Vector2Scale(delta, -1.0f/camera.zoom);
            camera.target = Vector2Add(camera.target, delta); // 更新相机的目标点
        }

        // 根据选择的缩放模式处理缩放
        if (zoomMode == 0) // 模式0: 鼠标滚轮缩放
        {
            float wheel = GetMouseWheelMove(); // 获取鼠标滚轮的滚动量 (向上为正，向下为负)
            if (wheel != 0) // 如果滚轮有滚动
            {
                // 关键步骤：实现以鼠标光标为中心的缩放
                // 1. 获取鼠标当前位置在世界坐标系中的点
                Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera);

                // 2. 将相机的偏移量 (offset) 设置为当前的鼠标屏幕坐标
                //    camera.offset 是屏幕上的一个点，这个点对应着世界坐标中的 camera.target
                camera.offset = GetMousePosition();

                // 3. 更新相机的目标点 (target) 为之前计算出的 mouseWorldPos
                //    这样做是为了在缩放后，原来在鼠标光标下的那个世界点，仍然保持在鼠标光标下。
                //    简单来说：在缩放前，鼠标指着世界A点，它在屏幕B点。
                //    缩放后，我们希望鼠标仍然指着世界A点，它仍然在屏幕B点。
                //    通过设置 offset = B, target = A 来实现。
                camera.target = mouseWorldPos;

                // 缩放增量计算
                // 使用对数和指数函数 (logf, expf) 来实现更平滑和一致的缩放速度，无论当前缩放级别如何。
                // wheel 的值通常是 1.0 或 -1.0
                float scaleFactor = 0.2f * wheel; // 调整这个0.2f可以改变缩放的灵敏度
                // camera.zoom = camera.zoom * powf(1.2f, wheel); // 另一种常见的指数缩放方式
                camera.zoom = expf(logf(camera.zoom) + scaleFactor);
                // Clamp 函数用于限制缩放级别在最小 (0.125f) 和最大 (64.0f) 之间
                camera.zoom = Clamp(camera.zoom, 0.125f, 64.0f);
            }
        }
        else // 模式1: 鼠标移动缩放 (按住右键并水平移动鼠标)
        {
            // 当鼠标右键刚被按下时，执行与滚轮缩放类似的操作来固定缩放中心
            if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))
            {
                Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera);
                camera.offset = GetMousePosition();
                camera.target = mouseWorldPos;
            }
            // 当鼠标右键持续按下时，根据鼠标水平移动量进行缩放
            if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
            {
                float deltaX = GetMouseDelta().x; // 获取鼠标在X轴上的移动量
                // 同样使用对数缩放
                float scaleFactor = 0.005f * deltaX; // 调整这个0.005f可以改变缩放的灵敏度
                camera.zoom = expf(logf(camera.zoom) + scaleFactor);
                camera.zoom = Clamp(camera.zoom, 0.125f, 64.0f);
            }
        }
        //----------------------------------------------------------------------------------

        // Draw
        //----------------------------------------------------------------------------------
        BeginDrawing(); // 开始绘制
            ClearBackground(RAYWHITE); // 用射线白清除背景

            BeginMode2D(camera); // 开始2D相机模式，之后的所有绘制操作都会受到相机变换的影响

                // 使用 rlgl 直接绘制一个3D网格，但将其旋转90度并平移，使其看起来像一个XY平面上的2D网格
                // rlPushMatrix/rlPopMatrix 用于保存和恢复OpenGL的变换矩阵状态，确保这里的变换不影响其他绘制
                rlPushMatrix();
                    rlTranslatef(0, 25*50, 0); // 沿Y轴平移 (在旋转后相当于沿新的Z轴)
                    rlRotatef(90, 1, 0, 0);    // 绕X轴旋转90度
                    DrawGrid(100, 50);         // 绘制网格 (100条线，间距50)
                rlPopMatrix();

                // 在世界坐标的屏幕中心 (如果相机target和offset都为0) 绘制一个参考圆
                // 注意：GetScreenWidth()/2, GetScreenHeight()/2 是屏幕坐标，但在BeginMode2D内，它们会被转换为世界坐标
                // 如果相机的 target 和 offset 都是 (0,0)，并且 zoom 是 1，那么这个圆会画在世界坐标的 (screenWidth/2, screenHeight/2)
                // 这个圆的位置会随着相机的平移和缩放而改变
                DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 50, MAROON);
                
            EndMode2D(); // 结束2D相机模式，之后的绘制操作不再受相机变换影响
            
            // 在屏幕上绘制鼠标光标的参考点和坐标 (屏幕坐标)
            DrawCircleV(GetMousePosition(), 4, DARKGRAY); // 在鼠标位置画一个小圆点
            // 显示鼠标的屏幕坐标
            DrawTextEx(GetFontDefault(), TextFormat("[%i, %i]", GetMouseX(), GetMouseY()), 
                Vector2Add(GetMousePosition(), (Vector2){ -44, -24 }), 20, 2, BLACK);

            // 绘制UI提示文本 (屏幕坐标，不受相机影响)
            DrawText("[1][2] Select mouse zoom mode (Wheel or Move)", 20, 20, 20, DARKGRAY);
            if (zoomMode == 0) DrawText("Mouse left button drag to move, mouse wheel to zoom", 20, 50, 20, DARKGRAY);
            else DrawText("Mouse left button drag to move, mouse press and move to zoom", 20, 50, 20, DARKGRAY);
        
        EndDrawing(); // 结束绘制
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    CloseWindow();        // 关闭窗口并释放OpenGL上下文资源
    //--------------------------------------------------------------------------------------
    return 0;
}